/**
 * Instruções:
 * - Compilação g++ -o <exec> atualiza.C `pkg-config --libs --cflags openssl gtkmm-2.4`
 * - Execução: ./<exec> "arquivo" "arquivo"
 */
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <gtkmm.h>
#include <openssl/md5.h>
#if defined(__WIN32__) || defined(__WIN__)
#include <windows.h>
#endif

using namespace std;

class CHECAGEM : public Gtk::Window
	{
	public:
		CHECAGEM(char *, char *);
		~CHECAGEM();
	protected:
		Gtk::VBox Principal;
		Gtk::HBox Conteudo[2];
		Gtk::Label Texto[3];
		Gtk::ProgressBar Barras[4];
		Gtk::Entry Resultado[3];

		bool verificar_arquivos();
		void ler_arquivo(int);
		void atualizar_barra(int);
	private:
		char *arquivo1, *arquivo2;
		unsigned char c[MD5_DIGEST_LENGTH];
		MD5_CTX contexto;
    	int bytes;
    	unsigned char dados[1024];
		bool carregado;
		void chamar();
		void copiar();
	};

CHECAGEM::CHECAGEM(char *primeiro, char *segundo)
	{
	this->arquivo1 = new char[strlen(primeiro)+1];
	strcpy(this->arquivo1,primeiro);
	this->arquivo2 = new char[strlen(segundo)+1];
	strcpy(this->arquivo2,segundo);
	this->set_title(Glib::ustring("Testando Arquivos"));
	this->set_position(Gtk::WIN_POS_CENTER);
	this->set_size_request (-1,-1);
	this->Principal.set_homogeneous (false);
	this->Principal.set_spacing (0);
	this->Conteudo[0].set_homogeneous (false);
	this->Conteudo[0].set_spacing (0);
	this->Texto[0].set_label("Primeiro Arquivo:  ");
	this->Conteudo[0].pack_start(this->Texto[0],false,false,0);
	this->Conteudo[0].pack_start(this->Barras[0],false,false,0);
	this->Resultado[0].set_size_request (270,-1);
	this->Conteudo[0].pack_start(this->Resultado[0],false,false,0);
	this->Principal.pack_start (this->Conteudo[0],false,false,0);
	this->Conteudo[1].set_homogeneous (false);
	this->Conteudo[1].set_spacing (0);
	this->Texto[1].set_label("Segundo Arquivo: ");
	this->Conteudo[1].pack_start(this->Texto[1],false,false,0);
	this->Conteudo[1].pack_start(this->Barras[1],false,false,0);
	this->Resultado[1].set_size_request (270,-1);
	this->Conteudo[1].pack_start(this->Resultado[1],false,false,0);
	this->Principal.pack_start (this->Conteudo[1],false,false,0);
	this->Principal.pack_start (this->Barras[2],true,false,0);
	this->Principal.pack_start (this->Barras[3],true,false,0);
	this->add(this->Principal);
	this->ler_arquivo(1);
	this->show_all();
	}

CHECAGEM::~CHECAGEM()
	{
	delete this->arquivo1;
	this->hide();
	}

bool CHECAGEM::verificar_arquivos()
	{
	string arq_1, arq_2;
	double new_val = Barras[2].get_fraction() + 0.1;

    if(new_val > 1.0)
		{
		arq_1.clear();
		arq_1 = this->Resultado[0].get_text();
		arq_2.clear();
		arq_2 = this->Resultado[1].get_text();
		if (arq_1.compare(arq_2.c_str()) == 0)
			{
			this->Barras[2].set_text("Arquivos Iguais");
			this->chamar();
			}
		else
			{
			this->Barras[2].set_text("Arquivos Diferentes");
			this->copiar();
			}
      	return false;
		}
    Barras[2].set_fraction(new_val);
	return true;
	}

void CHECAGEM::ler_arquivo(int num)
	{
	FILE *arquivo;
	char *aux;
	string buffer;
	int i;
	switch(num)
		{
		case 1:
			{
			arquivo = fopen(this->arquivo1, "rb");
			if (arquivo)
				{
				this->carregado = false;
				MD5_Init (&this->contexto);
				while ((this->bytes = fread (this->dados, 1, 1024, arquivo)) != 0)
					{
					MD5_Update (&this->contexto, this->dados, this->bytes);
					}
				MD5_Final (this->c,&this->contexto);
				buffer.clear();
				for(i = 0; i < MD5_DIGEST_LENGTH; i++)
					{
					aux = new char[32];
					sprintf(aux,"%02x", this->c[i]);
					buffer.append(aux);
					this->atualizar_barra (1);
					delete aux;
					}
				this->carregado = true;
				this->atualizar_barra(1);
				this->Resultado[0].set_text(Glib::ustring (buffer));
				fclose(arquivo);
				}
			else
				{
				this->Resultado[0].set_text("Erro ao Abrir Arquivo");
				}
			this->ler_arquivo(2);
			break;
			}
		case 2:
			{
			arquivo = fopen(this->arquivo2, "rb");
			if (arquivo)
				{
				this->carregado = false;
				MD5_Init (&this->contexto);
				while ((this->bytes = fread (this->dados, 1, 1024, arquivo)) != 0)
					{
					MD5_Update (&this->contexto, this->dados, this->bytes);
					}
				MD5_Final (this->c,&this->contexto);
				buffer.clear();
				for(i = 0; i < MD5_DIGEST_LENGTH; i++)
					{
					aux = new char[32];
					sprintf(aux,"%02x", this->c[i]);
					buffer.append(aux);
					this->atualizar_barra (1);
					delete aux;
					}
				this->carregado = true;
				this->atualizar_barra(2);
				this->Resultado[1].set_text(Glib::ustring (buffer));
				fclose(arquivo);
				}
			else
				{
				this->Resultado[1].set_text("Erro ao Abrir Arquivo");
				}
			Glib::signal_timeout().connect(sigc::mem_fun(*this,&CHECAGEM::verificar_arquivos), 200);
			break;
			}
		}
	}

void CHECAGEM::atualizar_barra (int codigo)
	{
	double valor;
	switch(codigo)
		{
		case 1:
			{
			valor = Barras[0].get_fraction() + 0.05;
			if ((valor > 1.0)||(this->carregado))
				{
				valor = 1.0;
				}
			Barras[0].set_fraction(valor);
			break;
			}
		case 2:
			{
			valor = Barras[1].get_fraction() + 0.05;
			if ((valor > 1.0)||(this->carregado))
				{
				valor = 1.0;
				}
			Barras[1].set_fraction(valor);
			break;
			}
		case 3:
			{
			valor = Barras[3].get_fraction() + 0.01;
			if ((valor > 1.0)||(this->carregado))
				{
				valor = 1.0;
				}
			Barras[3].set_fraction(valor);
			break;
			}
		}
	}

void CHECAGEM::chamar()
	{
	string CMD;
	char *comando;
	this->hide();
	CMD.clear();
	#if  !defined(__WIN32__) && !defined(__WIN__)
	CMD = "./";
	CMD.append(this->arquivo1);
	comando = new char[CMD.length()+1];
	strcpy(comando,CMD.c_str());
	system (comando);
	delete comando;
	#else
	ShellExecute(0, "open", this->arquivo1, NULL, NULL, SW_SHOWDEFAULT);
	#endif
	}

void CHECAGEM::copiar()
	{
	FILE *origem, *destino;
	char buffer[1024];
	size_t bytes;

	origem = fopen(this->arquivo1,"rb");
	destino = fopen(this->arquivo2,"wb");
	if ((origem) && (destino))
		{
		this->carregado = false;
		bytes = fread( buffer, sizeof( char ), 1024, origem );
		while( !feof( origem ) )
			{
			fwrite( buffer, sizeof( char ), bytes, destino );
			bytes = fread( buffer, sizeof( char ), 1024, origem );
			this->atualizar_barra (3);
			}
		this->carregado = true;
		this->atualizar_barra (3);
		this->Barras[3].set_text ("Arquivo copiado com sucesso!");
		fclose(origem);
		fclose(destino);
		}
	else
		{
		cout << "Erro ao abrir arquivos..." << endl;
		}
	}

int main (int argc, char **argv)
	{
	Gtk::Main kit(argc, argv);
	CHECAGEM window(argv[1],argv[2]);
	Gtk::Main::run(window);
	return 0;
	}